home *** CD-ROM | disk | FTP | other *** search
/ Clickx 115 / Clickx 115.iso / software / tools / windows / tails-i386-0.16.iso / live / filesystem.squashfs / usr / share / inkscape / extensions / inkex.py < prev    next >
Encoding:
Python Source  |  2010-03-12  |  7.9 KB  |  228 lines

  1. #!/usr/bin/env python
  2. """
  3. inkex.py
  4. A helper module for creating Inkscape extensions
  5.  
  6. Copyright (C) 2005,2007 Aaron Spike, aaron@ekips.org
  7.  
  8. This program is free software; you can redistribute it and/or modify
  9. it under the terms of the GNU General Public License as published by
  10. the Free Software Foundation; either version 2 of the License, or
  11. (at your option) any later version.
  12.  
  13. This program is distributed in the hope that it will be useful,
  14. but WITHOUT ANY WARRANTY; without even the implied warranty of
  15. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  16. GNU General Public License for more details.
  17.  
  18. You should have received a copy of the GNU General Public License
  19. along with this program; if not, write to the Free Software
  20. Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
  21. """
  22. import sys, copy, optparse, random, re
  23. import gettext
  24. from math import *
  25. _ = gettext.gettext
  26.  
  27. #a dictionary of all of the xmlns prefixes in a standard inkscape doc
  28. NSS = {
  29. u'sodipodi' :u'http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd',
  30. u'cc'       :u'http://creativecommons.org/ns#',
  31. u'ccOLD'    :u'http://web.resource.org/cc/',
  32. u'svg'      :u'http://www.w3.org/2000/svg',
  33. u'dc'       :u'http://purl.org/dc/elements/1.1/',
  34. u'rdf'      :u'http://www.w3.org/1999/02/22-rdf-syntax-ns#',
  35. u'inkscape' :u'http://www.inkscape.org/namespaces/inkscape',
  36. u'xlink'    :u'http://www.w3.org/1999/xlink',
  37. u'xml'      :u'http://www.w3.org/XML/1998/namespace'
  38. }
  39.  
  40. #a dictionary of unit to user unit conversion factors
  41. uuconv = {'in':90.0, 'pt':1.25, 'px':1, 'mm':3.5433070866, 'cm':35.433070866, 'pc':15.0}
  42. def unittouu(string):
  43.     '''Returns userunits given a string representation of units in another system'''
  44.     unit = re.compile('(%s)$' % '|'.join(uuconv.keys()))
  45.     param = re.compile(r'(([-+]?[0-9]+(\.[0-9]*)?|[-+]?\.[0-9]+)([eE][-+]?[0-9]+)?)')
  46.  
  47.     p = param.match(string)
  48.     u = unit.search(string)    
  49.     if p:
  50.         retval = float(p.string[p.start():p.end()])
  51.     else:
  52.         retval = 0.0
  53.     if u:
  54.         try:
  55.             return retval * uuconv[u.string[u.start():u.end()]]
  56.         except KeyError:
  57.             pass
  58.     return retval
  59.  
  60. def uutounit(val, unit):
  61.     return val/uuconv[unit]
  62.  
  63. try:
  64.     from lxml import etree
  65. except:
  66.     sys.exit(_('The fantastic lxml wrapper for libxml2 is required by inkex.py and therefore this extension. Please download and install the latest version from http://cheeseshop.python.org/pypi/lxml/, or install it through your package manager by a command like: sudo apt-get install python-lxml'))
  67.  
  68. def debug(what):
  69.     sys.stderr.write(str(what) + "\n")
  70.     return what
  71.  
  72. def errormsg(msg):
  73.     """Intended for end-user-visible error messages.
  74.     
  75.        (Currently just writes to stderr with an appended newline, but could do
  76.        something better in future: e.g. could add markup to distinguish error
  77.        messages from status messages or debugging output.)
  78.       
  79.        Note that this should always be combined with translation:
  80.  
  81.          import gettext
  82.          _ = gettext.gettext
  83.          ...
  84.          inkex.errormsg(_("This extension requires two selected paths."))
  85.     """
  86.     sys.stderr.write((str(msg) + "\n").encode("UTF-8"))
  87.  
  88. def check_inkbool(option, opt, value):
  89.     if str(value).capitalize() == 'True':
  90.         return True
  91.     elif str(value).capitalize() == 'False':
  92.         return False
  93.     else:
  94.         raise optparse.OptionValueError("option %s: invalid inkbool value: %s" % (opt, value))
  95.  
  96. def addNS(tag, ns=None):
  97.     val = tag
  98.     if ns!=None and len(ns)>0 and NSS.has_key(ns) and len(tag)>0 and tag[0]!='{':
  99.         val = "{%s}%s" % (NSS[ns], tag)
  100.     return val
  101.  
  102. class InkOption(optparse.Option):
  103.     TYPES = optparse.Option.TYPES + ("inkbool",)
  104.     TYPE_CHECKER = copy.copy(optparse.Option.TYPE_CHECKER)
  105.     TYPE_CHECKER["inkbool"] = check_inkbool
  106.  
  107. class Effect:
  108.     """A class for creating Inkscape SVG Effects"""
  109.  
  110.     def __init__(self, *args, **kwargs):
  111.         self.document=None
  112.         self.ctx=None
  113.         self.selected={}
  114.         self.doc_ids={}
  115.         self.options=None
  116.         self.args=None
  117.         self.OptionParser = optparse.OptionParser(usage="usage: %prog [options] SVGfile",option_class=InkOption)
  118.         self.OptionParser.add_option("--id",
  119.                         action="append", type="string", dest="ids", default=[], 
  120.                         help="id attribute of object to manipulate")
  121.  
  122.     def effect(self):
  123.         pass
  124.  
  125.     def getoptions(self,args=sys.argv[1:]):
  126.         """Collect command line arguments"""
  127.         self.options, self.args = self.OptionParser.parse_args(args)
  128.  
  129.     def parse(self,file=None):
  130.         """Parse document in specified file or on stdin"""
  131.         try:
  132.             try:
  133.                 stream = open(file,'r')
  134.             except:
  135.                 stream = open(self.svg_file,'r')
  136.         except:
  137.             stream = sys.stdin
  138.         self.document = etree.parse(stream)
  139.         stream.close()
  140.  
  141.     def getposinlayer(self):
  142.         #defaults
  143.         self.current_layer = self.document.getroot()
  144.         self.view_center = (0.0,0.0)
  145.  
  146.         layerattr = self.document.xpath('//sodipodi:namedview/@inkscape:current-layer', namespaces=NSS)
  147.         if layerattr:
  148.             layername = layerattr[0]
  149.             layer = self.document.xpath('//svg:g[@id="%s"]' % layername, namespaces=NSS)
  150.             if layer:
  151.                 self.current_layer = layer[0]
  152.  
  153.         xattr = self.document.xpath('//sodipodi:namedview/@inkscape:cx', namespaces=NSS)
  154.         yattr = self.document.xpath('//sodipodi:namedview/@inkscape:cy', namespaces=NSS)
  155.         doc_height = unittouu(self.document.getroot().get('height'))
  156.         if xattr and yattr:
  157.             x = xattr[0]
  158.             y = yattr[0]
  159.             if x and y:
  160.                 self.view_center = (float(x), doc_height - float(y)) # FIXME: y-coordinate flip, eliminate it when it's gone in Inkscape
  161.  
  162.     def getselected(self):
  163.         """Collect selected nodes"""
  164.         for i in self.options.ids:
  165.             path = '//*[@id="%s"]' % i
  166.             for node in self.document.xpath(path, namespaces=NSS):
  167.                 self.selected[i] = node
  168.  
  169.     def getElementById(self, id):
  170.         path = '//*[@id="%s"]' % id
  171.         el_list = self.document.xpath(path, namespaces=NSS)
  172.         if el_list:
  173.           return el_list[0]
  174.         else:
  175.           return None
  176.  
  177.     def getdocids(self):
  178.         docIdNodes = self.document.xpath('//@id', namespaces=NSS)
  179.         for m in docIdNodes:
  180.             self.doc_ids[m] = 1
  181.  
  182.     def getNamedView(self):
  183.         return self.document.xpath('//sodipodi:namedview', namespaces=NSS)[0]
  184.  
  185.     def createGuide(self, posX, posY, angle):
  186.         atts = {
  187.           'position': str(posX)+','+str(posY),
  188.           'orientation': str(sin(radians(angle)))+','+str(-cos(radians(angle)))
  189.           }
  190.         guide = etree.SubElement(
  191.                   self.getNamedView(),
  192.                   addNS('guide','sodipodi'), atts )
  193.         return guide
  194.  
  195.     def output(self):
  196.         """Serialize document into XML on stdout"""
  197.         self.document.write(sys.stdout)
  198.  
  199.     def affect(self, args=sys.argv[1:], output=True):
  200.         """Affect an SVG document with a callback effect"""
  201.         self.svg_file = args[-1]
  202.         self.getoptions(args)
  203.         self.parse()
  204.         self.getposinlayer()
  205.         self.getselected()
  206.         self.getdocids()
  207.         self.effect()
  208.         if output: self.output()
  209.  
  210.     def uniqueId(self, old_id, make_new_id = True):
  211.         new_id = old_id
  212.         if make_new_id:
  213.             while new_id in self.doc_ids:
  214.                 new_id += random.choice('0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ')
  215.             self.doc_ids[new_id] = 1
  216.         return new_id
  217.  
  218.     def xpathSingle(self, path):
  219.         try:
  220.             retval = self.document.xpath(path, namespaces=NSS)[0]
  221.         except:
  222.             errormsg(_("No matching node for expression: %s") % path)
  223.             retval = None
  224.         return retval
  225.             
  226.  
  227. # vim: expandtab shiftwidth=4 tabstop=8 softtabstop=4 encoding=utf-8 textwidth=99
  228.